﻿using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows.Threading;
using System.Windows;

namespace WpfStarterKit.Framework.ViewModel
{
    /// <summary>
    /// Base class for all classes which act as data source for user interface elements.
    /// This class mplements the INotifyPropertyChanged interface
    /// </summary>
    public abstract class BaseViewModel : INotifyPropertyChanged
    {
        /// <summary>
        /// Default constructor that initializes the Dispatcher depen
        /// </summary>
        public BaseViewModel()
        {
            if (Application.Current != null)
            {
                ModelDispatcher = Application.Current.Dispatcher;
            }
            else
            {
                ModelDispatcher = Dispatcher.CurrentDispatcher;
            }
        }

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Occurs when [property changed].
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        /// <summary>
        /// Called when [property changed].
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler eventHandlers = this.PropertyChanged;
            if (eventHandlers != null)
            {
                VerifyPropertyName(propertyName);
                HandlePropertyChanged(propertyName);
                eventHandlers(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// Called by OnPropertyChanged so that inheriting classes have a chance to do something when a 
        /// property changes
        /// </summary>
        /// <param name="propertyName">Property name</param>
        protected virtual void HandlePropertyChanged(string propertyName)
        { }

        /// <summary>
        /// Verifies the name of the property.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        private void VerifyPropertyName(string propertyName)
        {
            if (TypeDescriptor.GetProperties(this)[propertyName] == null)
            {
                string msg = string.Format(CultureInfo.CurrentCulture, "Invalid property name : {0}", propertyName);
                throw new ArgumentOutOfRangeException("propertyName", msg);
            }
        }

        Dispatcher _modelDispatcher;
        /// <summary>
        /// A Dispatcher object associated with the current view model. This can be used to access the UI thread
        /// from other threads (using Dispatcher.Invoke or Dispatcher.BeginInvoke)
        /// </summary>
        public Dispatcher ModelDispatcher
        {
            get
            {
                return _modelDispatcher;
            }
            set
            {
                _modelDispatcher = value;
                OnPropertyChanged("ModelDispatcher");
            }
        }
    }
}
